package com.jokins.common.util.passwore;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * 〈AES 加密工具类〉
 *  校验密码
 * AES 加密和解密的相关方法，使用 CBC 模式和 PKCS5Padding 填充方式。
 * @author Mr.Wang
 * @create 2025/2/5
 */
public class AESUtil {
    // AES 算法名称
    private static final String AES_ALGORITHM = "AES";
    // AES 加密模式和填充方式，使用 CBC 模式和 PKCS5Padding 填充
    private static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding";
    // AES 密钥长度，单位为比特，128 位
    private static final int AES_KEY_SIZE = 128;
    // 初始化向量（IV）的长度，单位为字节，AES 中 IV 长度为 16 字节
    private static final int IV_SIZE = 16;

    /**
     * 生成 AES 密钥
     *
     * @return 生成的密钥的 Base64 编码字符串
     * @throws Exception 生成密钥过程中可能出现的异常，如 NoSuchAlgorithmException 等
     */
    public static String generateAESKey() throws Exception {
        // 获取 AES 密钥生成器实例
        KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM);
        // 初始化密钥生成器，设置密钥长度为 128 位，并使用安全的随机数生成器
        keyGenerator.init(AES_KEY_SIZE, new SecureRandom());
        // 生成 AES 密钥
        SecretKey secretKey = keyGenerator.generateKey();
        // 将生成的密钥进行 Base64 编码并返回其字符串形式
        return Base64.getEncoder().encodeToString(secretKey.getEncoded());
    }

    /**
     * 生成随机的初始化向量（IV）
     *
     * @return 生成的 IV 的 Base64 编码字符串
     */
    public static String generateIV() {
        // 创建一个长度为 16 字节的字节数组用于存储 IV
        byte[] iv = new byte[IV_SIZE];
        // 创建一个安全的随机数生成器
        SecureRandom random = new SecureRandom();
        // 使用随机数生成器生成随机字节填充到 IV 数组中
        random.nextBytes(iv);
        // 将生成的 IV 进行 Base64 编码并返回其字符串形式
        return Base64.getEncoder().encodeToString(iv);
    }

    /**
     * AES 加密方法
     *
     * @param plainText 明文，即需要加密的原始文本
     * @param key       密钥的 Base64 编码字符串
     * @param iv        初始化向量的 Base64 编码字符串
     * @return 加密后的 Base64 编码字符串
     * @throws Exception 加密过程中可能出现的异常，如 NoSuchAlgorithmException、NoSuchPaddingException 等
     */
    public static String encrypt(String plainText, String key, String iv) throws Exception {
        // 将 Base64 编码的密钥字符串解码为字节数组，并创建 SecretKeySpec 对象
        SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(key), AES_ALGORITHM);
        // 将 Base64 编码的 IV 字符串解码为字节数组，并创建 IvParameterSpec 对象
        IvParameterSpec ivSpec = new IvParameterSpec(Base64.getDecoder().decode(iv));
        // 获取 AES 加密器实例，指定加密模式和填充方式
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        // 初始化加密器为加密模式，并传入密钥和 IV
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
        // 将明文转换为字节数组并进行加密操作
        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        // 将加密后的字节数组进行 Base64 编码并返回其字符串形式
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * AES 解密方法
     *
     * @param cipherText 密文的 Base64 编码字符串
     * @param key        密钥的 Base64 编码字符串
     * @param iv         初始化向量的 Base64 编码字符串
     * @return 解密后的明文
     * @throws Exception 解密过程中可能出现的异常，如 NoSuchAlgorithmException、NoSuchPaddingException 等
     */
    public static String decrypt(String cipherText, String key, String iv) throws Exception {
        // 将 Base64 编码的密钥字符串解码为字节数组，并创建 SecretKeySpec 对象
        SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(key), AES_ALGORITHM);
        // 将 Base64 编码的 IV 字符串解码为字节数组，并创建 IvParameterSpec 对象
        IvParameterSpec ivSpec = new IvParameterSpec(Base64.getDecoder().decode(iv));
        // 获取 AES 加密器实例，指定加密模式和填充方式
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        // 初始化解密器为解密模式，并传入密钥和 IV
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
        // 将 Base64 编码的密文字符串解码为字节数组并进行解密操作
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(cipherText));
        // 将解密后的字节数组转换为字符串并返回
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    /**
     * 校验加密密文的方法
     *
     * @param cipherText 密文的 Base64 编码字符串
     * @param plainText  原始明文
     * @param key        密钥的 Base64 编码字符串
     * @param iv         初始化向量的 Base64 编码字符串
     * @return 密文是否正确
     */
    public static boolean verifyCipherText(String cipherText, String plainText, String key, String iv) {
        try {
            // 对密文进行解密
            String decryptedText = decrypt(cipherText, key, iv);
            // 比较解密后的明文与原始明文是否相等
            return decryptedText.equals(plainText);
        } catch (Exception e) {
            // 解密过程中出现异常，认为密文不正确
            e.printStackTrace();
            return false;
        }
    }

    public static void main(String[] args) {
        try {
            // 生成 AES 密钥
            String key = generateAESKey();
            System.out.println("生成的 AES 密钥: " + key);

            // 生成初始化向量
            String iv = generateIV();
            System.out.println("生成的初始化向量: " + iv);

            // 明文
            String plainText = "k7Kpu8bVoN4LnhwkidFvI7YAg0A2VX1JX4yjSAeAkTM=";
            System.out.println("明文: " + plainText);

            // 加密
            String encryptedText = encrypt(plainText, key, iv);
            System.out.println("加密后的密文: " + encryptedText);

            // 解密
            String decryptedText = decrypt(encryptedText, key, iv);
            System.out.println("解密后的明文: " + decryptedText);

            // 校验密文
            boolean isCipherTextValid = verifyCipherText(encryptedText, plainText, key, iv);
            System.out.println("密文校验结果: " + isCipherTextValid);
        } catch (Exception e) {
            // 在生产环境中建议记录日志
            e.printStackTrace();
        }
    }
}